<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Shared Clipboard - P2P Text Sync | VDO.Ninja</title>
    
    <!-- SEO Meta Tags -->
    <meta name="description" content="Share text instantly across devices with VDO.Ninja's P2P Shared Clipboard. No server storage, real-time sync, secure peer-to-peer connection. Perfect for quick text sharing between devices.">
    <meta name="keywords" content="shared clipboard, p2p text sync, webrtc clipboard, cross-device clipboard, vdo.ninja, peer to peer, real-time text sharing, secure clipboard">
    <meta name="author" content="VDO.Ninja">
    <meta name="robots" content="index, follow">
    <link rel="canonical" href="https://vdo.ninja/clipboard">
    
    <!-- Open Graph / Facebook -->
    <meta property="og:type" content="website">
    <meta property="og:url" content="https://vdo.ninja/clipboard">
    <meta property="og:title" content="Shared Clipboard - P2P Text Sync | VDO.Ninja">
    <meta property="og:description" content="Share text instantly across devices with peer-to-peer technology. No server storage, real-time sync, completely secure.">
    <meta property="og:image" content="https://vdo.ninja/media/vdo-ninja-banner.png">
    <meta property="og:site_name" content="VDO.Ninja">
    
    <!-- Twitter -->
    <meta property="twitter:card" content="summary_large_image">
    <meta property="twitter:url" content="https://vdo.ninja/clipboard">
    <meta property="twitter:title" content="Shared Clipboard - P2P Text Sync | VDO.Ninja">
    <meta property="twitter:description" content="Share text instantly across devices with peer-to-peer technology. No server storage, real-time sync.">
    <meta property="twitter:image" content="https://vdo.ninja/media/vdo-ninja-banner.png">
    
    <!-- Additional Meta -->
    <meta name="application-name" content="VDO.Ninja Shared Clipboard">
    <meta name="theme-color" content="#2563eb">
    <meta name="mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="default">
    <meta name="apple-mobile-web-app-title" content="Shared Clipboard">
    
    <!-- Favicon -->
    <link rel="icon" type="image/png" href="https://vdo.ninja/favicon.png">
    <link rel="apple-touch-icon" href="https://vdo.ninja/apple-touch-icon.png">
    
    <!-- Structured Data -->
    <script type="application/ld+json">
    {
        "@context": "https://schema.org",
        "@type": "WebApplication",
        "name": "VDO.Ninja Shared Clipboard",
        "description": "Real-time P2P text synchronization across devices using WebRTC technology",
        "url": "https://vdo.ninja/clipboard",
        "applicationCategory": "UtilitiesApplication",
        "operatingSystem": "Any",
        "offers": {
            "@type": "Offer",
            "price": "0",
            "priceCurrency": "USD"
        },
        "creator": {
            "@type": "Organization",
            "name": "VDO.Ninja",
            "url": "https://vdo.ninja"
        },
        "featureList": [
            "Peer-to-peer text synchronization",
            "No server storage",
            "Real-time updates",
            "Cross-device compatibility",
            "Secure WebRTC connections"
        ]
    }
    </script>
    <style>
        * {
            box-sizing: border-box;
        }
        
        /* Light mode (default) */
        :root {
            --bg-primary: #fafafa;
            --bg-secondary: #ffffff;
            --bg-tertiary: #f5f5f5;
            --bg-input: #f8f8f8;
            --text-primary: #1a1a1a;
            --text-secondary: #4a4a4a;
            --text-tertiary: #6a6a6a;
            --border-primary: #e0e0e0;
            --border-secondary: #d0d0d0;
            --accent-primary: #2563eb;
            --accent-hover: #1d4ed8;
            --success: #059669;
            --danger: #dc2626;
            --info-bg: #e8f0ff;
            --info-text: #1e40af;
            --info-border: #b8d4ff;
            --shadow: rgba(0,0,0,0.08);
        }
        
        /* Dark mode */
        @media (prefers-color-scheme: dark) {
            :root {
                --bg-primary: #1a1a1a;
                --bg-secondary: #242424;
                --bg-tertiary: #2a2a2a;
                --bg-input: #1e1e1e;
                --text-primary: #e8e8e8;
                --text-secondary: #b8b8b8;
                --text-tertiary: #888888;
                --border-primary: #3a3a3a;
                --border-secondary: #4a4a4a;
                --accent-primary: #3b82f6;
                --accent-hover: #2563eb;
                --success: #10b981;
                --danger: #ef4444;
                --info-bg: #1e293b;
                --info-text: #94a3b8;
                --info-border: #334155;
                --shadow: rgba(0,0,0,0.3);
            }
        }
        
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
            background-color: var(--bg-primary);
            color: var(--text-primary);
            min-height: 100vh;
            transition: background-color 0.3s ease, color 0.3s ease;
        }
        
        .container {
            background: var(--bg-secondary);
            border-radius: 12px;
            box-shadow: 0 4px 20px var(--shadow);
            padding: 30px;
            border: 1px solid var(--border-primary);
        }
        
        h1 {
            margin: 0 0 10px 0;
            color: var(--text-primary);
            font-size: 28px;
            font-weight: 600;
        }
        
        .subtitle {
            color: var(--text-secondary);
            margin-bottom: 30px;
            font-size: 14px;
        }
        
        .share-section {
            background: var(--bg-tertiary);
            border: 2px dashed var(--border-secondary);
            border-radius: 8px;
            padding: 20px;
            margin-bottom: 25px;
        }
        
        .share-label {
            font-weight: 600;
            margin-bottom: 10px;
            color: var(--text-secondary);
            font-size: 14px;
        }
        
        .share-link-container {
            display: flex;
            gap: 10px;
            align-items: stretch;
        }
        
        #shareLink {
            flex: 1;
            padding: 12px 16px;
            border: 1px solid var(--border-primary);
            border-radius: 6px;
            font-size: 14px;
            background: var(--bg-input);
            color: var(--text-primary);
            font-family: monospace;
        }
        
        .copy-button {
            padding: 12px 20px;
            background: var(--accent-primary);
            color: white;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            font-size: 14px;
            font-weight: 500;
            transition: all 0.2s;
            white-space: nowrap;
        }
        
        .copy-button:hover {
            background: var(--accent-hover);
        }
        
        .copy-button:active {
            transform: scale(0.98);
        }
        
        .copy-button.copied {
            background: var(--success);
        }
        
        .clipboard-section {
            margin-bottom: 20px;
        }
        
        .clipboard-label {
            font-weight: 600;
            margin-bottom: 10px;
            color: var(--text-secondary);
            font-size: 14px;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        
        #sharedClipboard {
            width: 100%;
            min-height: 300px;
            padding: 16px;
            border: 2px solid var(--border-primary);
            border-radius: 8px;
            font-size: 15px;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            resize: vertical;
            background: var(--bg-input);
            color: var(--text-primary);
            transition: border-color 0.2s, background-color 0.3s ease;
        }
        
        #sharedClipboard:focus {
            outline: none;
            border-color: var(--accent-primary);
            background: var(--bg-secondary);
        }
        
        #sharedClipboard::placeholder {
            color: var(--text-tertiary);
        }
        
        .status {
            display: flex;
            align-items: center;
            gap: 8px;
            padding: 10px 16px;
            background: var(--bg-tertiary);
            border-radius: 6px;
            font-size: 13px;
            margin-bottom: 15px;
        }
        
        .status-indicator {
            width: 8px;
            height: 8px;
            border-radius: 50%;
            background: var(--danger);
            transition: background 0.3s;
        }
        
        .status-indicator.connected {
            background: var(--success);
            animation: pulse 2s infinite;
        }
        
        @keyframes pulse {
            0% { opacity: 1; }
            50% { opacity: 0.5; }
            100% { opacity: 1; }
        }
        
        .status-text {
            color: var(--text-tertiary);
        }
        
        .peers-count {
            color: var(--text-secondary);
            font-weight: 500;
        }
        
        .char-count {
            color: var(--text-tertiary);
            font-size: 12px;
        }
        
        .info-section {
            margin-top: 30px;
            padding: 20px;
            background: var(--info-bg);
            border-radius: 8px;
            font-size: 14px;
            line-height: 1.6;
            border: 1px solid var(--info-border);
        }
        
        .info-section h3 {
            margin: 0 0 10px 0;
            color: var(--info-text);
            font-size: 16px;
        }
        
        .info-section ul {
            margin: 10px 0;
            padding-left: 20px;
        }
        
        .info-section li {
            margin: 5px 0;
            color: var(--info-text);
        }

		.copy-button[style*="--danger"]:hover {
			background: #b91c1c;
		}

        @media (max-width: 600px) {
            body {
                padding: 10px;
                font-size: 16px;
            }
            
            .container {
                padding: 20px;
            }
            
            h1 {
                font-size: 24px;
            }
            
            .share-link-container {
                flex-direction: column;
                gap: 12px;
            }
            
            #shareLink {
                font-size: 16px;
                padding: 14px 16px;
            }
            
            .copy-button {
                width: 100%;
                padding: 14px 20px;
                font-size: 16px;
            }
            
            #sharedClipboard {
                font-size: 16px;
                min-height: 250px;
            }
            
            .info-section {
                margin-top: 20px;
            }
        }
        
        @media (max-width: 400px) {
            body {
                padding: 8px;
            }
            
            .container {
                padding: 16px;
            }
            
            h1 {
                font-size: 20px;
            }
            
            .subtitle {
                font-size: 13px;
            }
        }
		
		@media (max-width: 600px) {
			/* Update the existing media query section */
			div[style*="display: flex"] {
				gap: 8px;
			}
			
			div[style*="display: flex"] .copy-button {
				padding: 14px 10px;
				font-size: 15px;
			}
		}
    </style>
</head>
<body>
    <div class="container">
        <h1>🔗 Shared Clipboard</h1>
        <p class="subtitle">Real-time P2P text synchronization across devices</p>
        
        <div class="share-section">
            <div class="share-label">Share this link to sync clipboards:</div>
            <div class="share-link-container">
                <input type="text" id="shareLink" readonly>
                <button class="copy-button" onclick="copyShareLink()">Copy Link</button>
            </div>
        </div>
        
        <div class="clipboard-section">
            <div class="clipboard-label">
                <span>Shared Clipboard</span>
                <span class="char-count" id="charCount">0 characters</span>
            </div>
            <textarea 
                id="sharedClipboard" 
                placeholder="Type or paste text here. It will automatically sync with all connected devices..."
            ></textarea>
        </div>
        
        <div class="status">
            <span class="status-indicator" id="statusIndicator"></span>
            <span class="status-text" id="statusText">Connecting...</span>
            <span class="peers-count" id="peersCount"></span>
        </div>
        
        <button class="copy-button" style="width: 100%; margin-top: 15px;" onclick="copyClipboardContent()">
            Copy Clipboard Content
        </button>
		
		<div style="display: flex; gap: 10px; margin-top: 10px;">
			<button class="copy-button" style="flex: 1; background: var(--danger);" onclick="clearClipboard()">
				Clear
			</button>
			<button class="copy-button" style="flex: 1; background: var(--danger);" onclick="clearAndPaste()">
				Clear & Paste
			</button>
		</div>
        
        <div class="info-section">
            <h3>ℹ️ How it works</h3>
            <ul>
                <li>Share the link above with other devices or users</li>
                <li>Any text typed or pasted will sync automatically</li>
                <li>All data is transmitted peer-to-peer (no server storage)</li>
                <li>Perfect for quickly sharing text between devices</li>
            </ul>
        </div>
    </div>
    
    <!-- Load the VDO.Ninja SDK -->
    <script src="vdoninja-sdk.js"></script>
    
    <script>
        let sdk = null;
        let roomId = '';
        let streamId = '';
        let connectedPeers = new Set();
        let isUpdatingFromRemote = false;
        let syncDebounceTimer = null;
        
        // Initialize on page load
        window.addEventListener('load', async () => {
            await initializeSharedClipboard();
        });
        
        async function initializeSharedClipboard() {
            // Get or generate room ID from URL
            const urlParams = new URLSearchParams(window.location.search);
            roomId = urlParams.get('room');
            
            if (!roomId) {
                // Generate a new room ID
                roomId = generateRoomId();
                // Update URL without reloading
                const newUrl = window.location.origin + window.location.pathname + '?room=' + roomId;
                window.history.replaceState({}, '', newUrl);
            }
            
            // Generate unique stream ID for this instance
            streamId = 'clipboard-' + Math.random().toString(36).substr(2, 9);
            
            // Update share link
            const shareLink = window.location.origin + window.location.pathname + '?room=' + roomId;
            document.getElementById('shareLink').value = shareLink;
            
            // Initialize SDK
            sdk = new VDONinjaSDK({
                room: roomId,
                password: false, // No encryption for simplicity
                debug: true
            });
            
            // Set up event listeners
            setupSDKEventListeners();
            
            // Set up textarea event listener
            const textarea = document.getElementById('sharedClipboard');
            textarea.addEventListener('input', handleTextareaChange);
            
            // Connect to the network
            try {
                await sdk.connect();
                
                // Announce ourselves as a data-only peer
                await sdk.announce({
                    streamID: streamId,
                    room: roomId,
                    label: 'shared-clipboard'
                });
                
                // Join room to discover other peers
                await sdk.joinRoom({ room: roomId });
                
                updateStatus('Connected', true);
                console.log('Connected to room:', roomId);
                
            } catch (error) {
                console.error('Connection error:', error);
                updateStatus('Connection failed', false);
            }
        }
		
		function clearClipboard() {
			const textarea = document.getElementById('sharedClipboard');
			textarea.value = '';
			
			// Update character count
			document.getElementById('charCount').textContent = '0 characters';
			
			// Send update to peers
			sendContentUpdate('');
			
			// Visual feedback
			const button = event.target;
			const originalText = button.textContent;
			button.textContent = 'Cleared!';
			
			setTimeout(() => {
				button.textContent = originalText;
			}, 1500);
		}
        
		async function clearAndPaste() {
			const textarea = document.getElementById('sharedClipboard');
			
			try {
				// Clear first
				textarea.value = '';
				
				// Try to read from clipboard
				const text = await navigator.clipboard.readText();
				textarea.value = text;
				
				// Update character count
				document.getElementById('charCount').textContent = text.length + ' characters';
				
				// Send update to peers
				sendContentUpdate(text);
				
				// Visual feedback
				const button = event.target;
				const originalText = button.textContent;
				button.textContent = 'Pasted!';
				
				setTimeout(() => {
					button.textContent = originalText;
				}, 1500);
			} catch (err) {
				// Fallback if clipboard API fails
				console.error('Failed to read clipboard:', err);
				
				// Visual feedback for error
				const button = event.target;
				const originalText = button.textContent;
				button.textContent = 'Paste failed';
				
				setTimeout(() => {
					button.textContent = originalText;
				}, 2000);
			}
		}

        function setupSDKEventListeners() {
            // Connection status
            sdk.addEventListener('connected', () => {
                console.log('Connected to signaling server');
            });
            
            sdk.addEventListener('disconnected', () => {
                updateStatus('Disconnected', false);
                connectedPeers.clear();
                updatePeersCount();
            });
            
            // Peer events
            sdk.addEventListener('peerConnected', (event) => {
                const peerId = event.detail.uuid;
                connectedPeers.add(peerId);
                updatePeersCount();
                console.log('Peer connected:', peerId);
                
                // Send current clipboard content to new peer
                sendCurrentContent(peerId);
            });
            
            sdk.addEventListener('peerDisconnected', (event) => {
                const peerId = event.detail.uuid;
                connectedPeers.delete(peerId);
                updatePeersCount();
                console.log('Peer disconnected:', peerId);
            });
            
            // Data channel events
            sdk.addEventListener('dataChannelOpen', (event) => {
                console.log('Data channel opened with:', event.detail.uuid);
            });
            
            // Handle incoming data
            sdk.addEventListener('dataReceived', (event) => {
                handleIncomingData(event.detail);
            });
            
            // Handle room listings
            sdk.addEventListener('listing', async (event) => {
                if (event.detail.list && event.detail.list.length > 0) {
                    console.log('Found peers in room:', event.detail.list.length);
                    
                    // Connect to other peers in mesh mode
                    for (const peer of event.detail.list) {
                        if (peer.streamID && peer.streamID !== streamId) {
                            try {
                                await sdk.quickView({
                                    streamID: peer.streamID,
                                    password: false,
                                    audio: false,
                                    video: false
                                });
                                console.log('Connected to peer:', peer.streamID);
                            } catch (error) {
                                console.error('Failed to connect to peer:', peer.streamID, error);
                            }
                        }
                    }
                }
            });
        }
        
        function handleTextareaChange() {
            if (isUpdatingFromRemote) return;
            
            const textarea = document.getElementById('sharedClipboard');
            const content = textarea.value;
            
            // Update character count
            document.getElementById('charCount').textContent = content.length + ' characters';
            
            // Debounce sync to avoid too many messages
            clearTimeout(syncDebounceTimer);
            syncDebounceTimer = setTimeout(() => {
                sendContentUpdate(content);
            }, 100); // 100ms debounce
        }
        
        function sendContentUpdate(content) {
            if (!sdk || connectedPeers.size === 0) return;
            
            const message = {
                type: 'clipboard-update',
                content: content,
                timestamp: Date.now(),
                sender: streamId
            };
            
            // Send to all connected peers
            sdk.sendData(message);
            console.log('Sent content update to peers');
        }
        
        function sendCurrentContent(peerId) {
            if (!sdk) return;
            
            const textarea = document.getElementById('sharedClipboard');
            const message = {
                type: 'clipboard-sync',
                content: textarea.value,
                timestamp: Date.now(),
                sender: streamId
            };
            
            // Send to specific peer
            sdk.sendData(message, peerId);
            console.log('Sent current content to peer:', peerId);
        }
        
        function handleIncomingData(detail) {
            const { data, uuid } = detail;
            
            if (!data || typeof data !== 'object') return;
            
            if (data.type === 'clipboard-update' || data.type === 'clipboard-sync') {
                // Update textarea with received content
                isUpdatingFromRemote = true;
                const textarea = document.getElementById('sharedClipboard');
                textarea.value = data.content || '';
                
                // Update character count
                document.getElementById('charCount').textContent = textarea.value.length + ' characters';
                
                // Reset flag after a short delay
                setTimeout(() => {
                    isUpdatingFromRemote = false;
                }, 50);
                
                console.log('Received content update from:', data.sender);
            }
        }
        
        function generateRoomId() {
            // Generate a readable room ID
            const adjectives = ['quick', 'bright', 'swift', 'smart', 'cool', 'neat', 'fast', 'sharp'];
            const nouns = ['fox', 'hawk', 'wolf', 'bear', 'lion', 'eagle', 'shark', 'tiger'];
            const adj = adjectives[Math.floor(Math.random() * adjectives.length)];
            const noun = nouns[Math.floor(Math.random() * nouns.length)];
            const num = Math.floor(Math.random() * 1000);
            return `${adj}-${noun}-${num}`;
        }
        
        function updateStatus(text, isConnected) {
            const indicator = document.getElementById('statusIndicator');
            const statusText = document.getElementById('statusText');
            
            statusText.textContent = text;
            if (isConnected) {
                indicator.classList.add('connected');
            } else {
                indicator.classList.remove('connected');
            }
        }
        
        function updatePeersCount() {
            const peersCount = document.getElementById('peersCount');
            const count = connectedPeers.size;
            
            if (count === 0) {
                peersCount.textContent = '';
            } else if (count === 1) {
                peersCount.textContent = '• 1 device connected';
            } else {
                peersCount.textContent = `• ${count} devices connected`;
            }
        }
        
        function copyShareLink() {
            const shareLink = document.getElementById('shareLink');
            shareLink.select();
            document.execCommand('copy');
            
            // Update button text temporarily
            const button = event.target;
            const originalText = button.textContent;
            button.textContent = 'Copied!';
            button.classList.add('copied');
            
            setTimeout(() => {
                button.textContent = originalText;
                button.classList.remove('copied');
            }, 2000);
        }
        
        function copyClipboardContent() {
            const textarea = document.getElementById('sharedClipboard');
            textarea.select();
            document.execCommand('copy');
            
            // Update button text temporarily
            const button = event.target;
            const originalText = button.textContent;
            button.textContent = 'Copied!';
            button.classList.add('copied');
            
            setTimeout(() => {
                button.textContent = originalText;
                button.classList.remove('copied');
            }, 2000);
        }
        
        // Clean up on page unload
        window.addEventListener('beforeunload', () => {
            if (sdk) {
                sdk.disconnect();
            }
        });
    </script>
</body>
</html>